use extension_generator::{
    cli::{ExtensionType, LayerType},
    config::Config,
    generator::{GenerationContext, Generator},
};
use std::path::PathBuf;
use tempfile::TempDir;

/// Integration tests for the extension generator
///
/// These tests verify that the generator can successfully create
/// extension projects with different configurations.

#[tokio::test]
async fn test_generate_standard_extension() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let output_path = temp_dir.path().join("test-standard");

    let context = GenerationContext::new(
        "test-standard".to_string(),
        Some("Test standard extension".to_string()),
        ExtensionType::Standard,
        None,
        Some("Test Author".to_string()),
        Some(output_path.clone()),
    );

    let generator = Generator::new();
    let result = generator.generate(&context).await;

    assert!(result.is_ok(), "Failed to generate standard extension: {:?}", result);

    // Verify that expected files were created
    assert!(output_path.join("Cargo.toml").exists(), "Cargo.toml not found");
    assert!(output_path.join("README.md").exists(), "README.md not found");
    assert!(output_path.join("src").join("lib.rs").exists(), "lib.rs not found");

    // Verify layer directories
    assert!(output_path.join("src").join("nodes").exists(), "nodes directory not found");
    assert!(output_path.join("src").join("plugins").exists(), "plugins directory not found");
    assert!(output_path.join("src").join("command").exists(), "command directory not found");
    assert!(output_path.join("src").join("router").exists(), "router directory not found");
}

#[tokio::test]
async fn test_generate_api_extension() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let output_path = temp_dir.path().join("test-api");

    let context = GenerationContext::new(
        "test-api".to_string(),
        Some("Test API extension".to_string()),
        ExtensionType::Api,
        None,
        Some("API Team".to_string()),
        Some(output_path.clone()),
    );

    let generator = Generator::new();
    let result = generator.generate(&context).await;

    assert!(result.is_ok(), "Failed to generate API extension: {:?}", result);

    // Verify API-specific files
    assert!(output_path.join("src").join("router").join("handlers.rs").exists(), "handlers.rs not found");
    assert!(output_path.join("src").join("router").join("dto.rs").exists(), "dto.rs not found");
}

#[tokio::test]
async fn test_generate_lightweight_extension() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let output_path = temp_dir.path().join("test-lightweight");

    let context = GenerationContext::new(
        "test-lightweight".to_string(),
        Some("Test lightweight extension".to_string()),
        ExtensionType::Lightweight,
        None,
        Some("Light Team".to_string()),
        Some(output_path.clone()),
    );

    let generator = Generator::new();
    let result = generator.generate(&context).await;

    assert!(result.is_ok(), "Failed to generate lightweight extension: {:?}", result);

    // Verify only expected layers exist
    assert!(output_path.join("src").join("nodes").exists(), "nodes directory should exist");
    assert!(output_path.join("src").join("plugins").exists(), "plugins directory should exist");
    assert!(!output_path.join("src").join("command").exists(), "command directory should not exist");
    assert!(!output_path.join("src").join("router").exists(), "router directory should not exist");
}

#[tokio::test]
async fn test_generate_custom_extension() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let output_path = temp_dir.path().join("test-custom");

    let custom_layers = vec![LayerType::Nodes, LayerType::Commands];

    let context = GenerationContext::new(
        "test-custom".to_string(),
        Some("Test custom extension".to_string()),
        ExtensionType::Custom,
        Some(custom_layers),
        Some("Custom Team".to_string()),
        Some(output_path.clone()),
    );

    let generator = Generator::new();
    let result = generator.generate(&context).await;

    assert!(result.is_ok(), "Failed to generate custom extension: {:?}", result);

    // Verify only selected layers exist
    assert!(output_path.join("src").join("nodes").exists(), "nodes directory should exist");
    assert!(output_path.join("src").join("command").exists(), "command directory should exist");
    assert!(!output_path.join("src").join("plugins").exists(), "plugins directory should not exist");
    assert!(!output_path.join("src").join("router").exists(), "router directory should not exist");
}

#[tokio::test]
async fn test_generation_context() {
    let context = GenerationContext::new(
        "test-context".to_string(),
        Some("Test context".to_string()),
        ExtensionType::Standard,
        None,
        Some("Context Author".to_string()),
        Some(PathBuf::from("./test-output")),
    );

    // Test name transformations
    assert_eq!(context.names.original, "test-context");
    assert_eq!(context.names.kebab, "test-context");
    assert_eq!(context.names.snake, "test_context");
    assert_eq!(context.names.pascal, "TestContext");
    assert_eq!(context.names.camel, "testContext");

    // Test project names
    assert_eq!(context.project_name(), "extension-test-context");
    assert_eq!(context.interface_project_name(), "extension-test-context-interface");
    assert_eq!(context.plugin_struct_name(), "PluginTestContext");

    // Test layer checks
    assert!(context.has_layer(&LayerType::Nodes));
    assert!(context.has_layer(&LayerType::Plugins));
    assert!(context.has_layer(&LayerType::Commands));
    assert!(context.has_layer(&LayerType::Router));
}

#[test]
fn test_config_operations() {
    let mut config = Config::default();

    // Test setting values
    assert!(config.set("default_author", "Test Author".to_string()).is_ok());
    assert!(config.set("auto_format", "true".to_string()).is_ok());
    assert!(config.set("verbose_level", "2".to_string()).is_ok());
    assert!(config.set("custom_key", "custom_value".to_string()).is_ok());

    // Test getting values
    assert_eq!(config.get("default_author"), Some("Test Author".to_string()));
    assert_eq!(config.get("auto_format"), Some("true".to_string()));
    assert_eq!(config.get("verbose_level"), Some("2".to_string()));
    assert_eq!(config.get("custom_key"), Some("custom_value".to_string()));
    assert_eq!(config.get("nonexistent"), None);

    // Test validation
    assert!(config.validate().is_ok());

    // Test invalid values
    assert!(config.set("verbose_level", "invalid".to_string()).is_err());
    assert!(config.set("auto_format", "not_a_boolean".to_string()).is_err());
}

#[test]
fn test_extension_types() {
    let standard = ExtensionType::Standard;
    let api = ExtensionType::Api;
    let core = ExtensionType::Core;
    let lightweight = ExtensionType::Lightweight;
    let custom = ExtensionType::Custom;

    // Test default layers
    assert_eq!(standard.default_layers().len(), 4);
    assert_eq!(api.default_layers().len(), 4);
    assert_eq!(core.default_layers().len(), 3);
    assert_eq!(lightweight.default_layers().len(), 2);
    assert_eq!(custom.default_layers().len(), 0);

    // Test descriptions
    assert!(!standard.description().is_empty());
    assert!(!api.description().is_empty());
    assert!(!core.description().is_empty());
    assert!(!lightweight.description().is_empty());
    assert!(!custom.description().is_empty());
}

#[test]
fn test_layer_types() {
    let nodes = LayerType::Nodes;
    let plugins = LayerType::Plugins;
    let commands = LayerType::Commands;
    let router = LayerType::Router;

    // Test descriptions
    assert!(!nodes.description().is_empty());
    assert!(!plugins.description().is_empty());
    assert!(!commands.description().is_empty());
    assert!(!router.description().is_empty());

    // Test directory names
    assert_eq!(nodes.directory_name(), "nodes");
    assert_eq!(plugins.directory_name(), "plugins");
    assert_eq!(commands.directory_name(), "command");
    assert_eq!(router.directory_name(), "router");

    // Test web-related check
    assert!(!nodes.is_web_related());
    assert!(!plugins.is_web_related());
    assert!(!commands.is_web_related());
    assert!(router.is_web_related());
}

#[tokio::test]
async fn test_file_content_validation() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let output_path = temp_dir.path().join("test-validation");

    let context = GenerationContext::new(
        "test-validation".to_string(),
        Some("Test validation extension".to_string()),
        ExtensionType::Standard,
        None,
        Some("Validation Team".to_string()),
        Some(output_path.clone()),
    );

    let generator = Generator::new();
    let result = generator.generate(&context).await;

    assert!(result.is_ok(), "Failed to generate extension for validation test");

    // Test that Cargo.toml is valid TOML
    let cargo_toml_path = output_path.join("Cargo.toml");
    let cargo_toml_content = std::fs::read_to_string(&cargo_toml_path).expect("Failed to read Cargo.toml");

    let _: toml::Value = toml::from_str(&cargo_toml_content).expect("Generated Cargo.toml is not valid TOML");

    // Test that lib.rs contains expected content
    let lib_rs_path = output_path.join("src").join("lib.rs");
    let lib_rs_content = std::fs::read_to_string(&lib_rs_path).expect("Failed to read lib.rs");

    assert!(lib_rs_content.contains("pub mod command;"), "lib.rs missing command module");
    assert!(lib_rs_content.contains("pub mod nodes;"), "lib.rs missing nodes module");
    assert!(lib_rs_content.contains("pub mod plugins;"), "lib.rs missing plugins module");
    assert!(lib_rs_content.contains("pub mod router;"), "lib.rs missing router module");

    // Test that generated code has basic Rust syntax validity
    // (This is a simple check - for full validation, we'd need to run cargo check)
    for rust_file in ["src/lib.rs", "src/nodes/mod.rs", "src/plugins/mod.rs", "src/command/mod.rs"] {
        let file_path = output_path.join(rust_file);
        if file_path.exists() {
            let content = std::fs::read_to_string(&file_path).expect(&format!("Failed to read {}", rust_file));

            // Basic syntax check: ensure braces are balanced
            let open_braces = content.matches('{').count();
            let close_braces = content.matches('}').count();
            assert_eq!(open_braces, close_braces, "Unbalanced braces in {}", rust_file);
        }
    }
}
